# *-* coding:utf8 *-*
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad
from Crypto.Cipher import DES
from Crypto.Cipher import DES3
import base64
import rsa
from abc import ABCMeta, abstractmethod
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT


class PHCrypto(metaclass=ABCMeta):

    @abstractmethod
    def cbc_encrypt(self):
        raise NotImplementedError

    @abstractmethod
    def ecb_encrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def ecb_decrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def cbc_encrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def cbc_decrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def ofb_encrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def ofb_decrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def cfb_encrypt(self, text):
        raise NotImplementedError

    @abstractmethod
    def cfb_decrypt(self, text):
        raise NotImplementedError

    def padding(self, data):
        if self.padding_mode == 'zero':
            return self.zero_padding(data)
        elif self.padding_mode == 'pkcs7':
            return self.pkcs7_padding(data)
        elif self.padding_mode == 'pkcs5':
            return self.pkcs5_padding(data)

    def unpadding(self, data):
        if self.padding_mode == 'zero':
            return self.zero_unpadding(data)
        elif self.padding_mode == 'pkcs7':
            return self.pkcs7_unpadding(data)
        elif self.padding_mode == 'pkcs5':
            return self.pkcs5_unpadding(data)

    @staticmethod
    def pkcs7_padding(data):
        return pad(data.encode('utf-8'), AES.block_size, style='pkcs7')

    @staticmethod
    def pkcs7_unpadding(data):
        return unpad(data.encode('utf-8'), AES.block_size, style='pkcs7').decode()

    @staticmethod
    def pkcs5_padding(data):
        return (data + (AES.block_size - len(data) % AES.block_size) * chr(AES.block_size - len(data) % AES.block_size)).encode('utf-8')

    @staticmethod
    def pkcs5_unpadding(data):
        return data[0:-ord(data[-1])]

    @staticmethod
    def zero_padding(data):
        add = 0
        if len(data.encode('utf-8')) % 16:
            add = 16 - (len(data.encode('utf-8')) % 16)
        text = data + ('\0' * add)
        return text.encode('utf-8')

    @staticmethod
    def zero_unpadding(data):
        return data.rstrip('\0')


class AESCrypto(PHCrypto):
    """
        AESCrypto
        key             ：必填 16 24 32位长度
        iv              ：选填 16位
        padding_mode    ：选填 默认0填充
    """
    def __init__(self, key, padding_mode='zero', iv=''):
        self.key = key.encode('utf-8')
        self.iv = iv[0:16].encode('utf-8')
        self.padding_mode = padding_mode

    def ecb_encrypt(self, text):
        crypto = AES.new(key=self.key, mode=AES.MODE_ECB)
        return self.__encrypt(crypto, text)

    def ecb_decrypt(self, text):
        crypto = AES.new(key=self.key, mode=AES.MODE_ECB)
        return self.__decrypt(crypto, text)

    def cbc_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_CBC, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cbc_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_CBC, iv=self.iv)
        return self.__decrypt(crypto, text)

    def ofb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_OFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def ofb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_OFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def cfb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_CFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cfb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = AES.new(key=self.key, mode=AES.MODE_CFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def __encrypt(self, crypto, text):
        data = crypto.encrypt(self.padding(text))
        return base64.b64encode(data).decode('utf-8')

    def __decrypt(self, crypto, data):
        text = base64.b64decode(data)
        return self.unpadding(crypto.decrypt(text).decode('utf-8'))


class RSACrypto:

    """
        RSACrypto
        public_key    ：必填 public_key 字符串
    """
    def __init__(self, public_key, exponent='', modulus=''):
        self.public_key = public_key
        self.exponent = exponent
        self.modulus = modulus

    def public_key_encrypt(self, text):
        self.str_to_key()
        rsa_exponent = int(self.exponent, 16)  # 十六进制转十进制
        rsa_modulus = int(self.modulus, 16)
        key = rsa.PublicKey(rsa_modulus, rsa_exponent)
        data = rsa.encrypt(text.encode('utf-8'), key)
        return base64.b64encode(data).decode('utf-8')

    def modulus_encrypt(self, text):
        rsa_exponent = int(self.exponent, 16)  # 十六进制转十进制
        rsa_modulus = int(self.modulus, 16)
        key = rsa.PublicKey(rsa_modulus, rsa_exponent)
        data = rsa.encrypt(text.encode('utf-8'), key)
        return base64.b64encode(data).decode('utf-8')

    def encrypt(self, text):
        if self.public_key != '':
            return self.public_key_encrypt(text)
        elif  self.exponent != '' and self.modulus != '':
            return self.modulus_encrypt(text)

    def str_to_key(self):
        b_str = base64.b64decode(self.public_key)
        if len(b_str) < 162:
            return False
        hex_str = ''

        for x in b_str:
            h = hex(x)[2:]
            h = h.rjust(2, '0')
            hex_str += h

        m_start = 29 * 2
        e_start = 159 * 2
        m_len = 128 * 2
        e_len = 3 * 2

        self.modulus = hex_str[m_start:m_start + m_len]
        self.exponent = hex_str[e_start:e_start + e_len]


class DESCrypto(PHCrypto):
    """
          DESCrypto
          key             ：必填 8 位长度
          iv              ：选填 8 位
          padding_mode    ：选填 默认0填充
    """

    def __init__(self, key, padding_mode='zero', iv=''):
        self.key = key.encode('utf-8')
        self.iv = iv[0:8].encode('utf-8')
        self.padding_mode = padding_mode

    def ecb_encrypt(self, text):
        crypto = DES.new(key=self.key, mode=DES.MODE_ECB)
        return self.__encrypt(crypto, text)

    def ecb_decrypt(self, text):
        crypto = DES.new(key=self.key, mode=DES.MODE_ECB)
        return self.__decrypt(crypto, text)

    def cbc_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_CBC, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cbc_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_CBC, iv=self.iv)
        return self.__decrypt(crypto, text)

    def ofb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_OFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def ofb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_OFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def cfb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_CFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cfb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES.new(key=self.key, mode=DES.MODE_CFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def __encrypt(self, crypto, text):
        data = crypto.encrypt(self.padding(text))
        return base64.b64encode(data).decode('utf-8')

    def __decrypt(self, crypto, data):
        text = base64.b64decode(data)
        return self.unpadding(crypto.decrypt(text).decode('utf-8'))


class DES3Crypto(PHCrypto):
    """
             3DESCrypto
             key             ：必填 16 位长度
             iv              ：选填 8 位
             padding_mode    ：选填 默认0填充
    """

    def __init__(self, key, padding_mode='zero', iv=''):
        self.key = key.encode('utf-8')
        self.iv = iv[0:8].encode('utf-8')
        self.padding_mode = padding_mode

    def ecb_encrypt(self, text):
        crypto = DES3.new(key=self.key, mode=DES3.MODE_ECB)
        return self.__encrypt(crypto, text)

    def ecb_decrypt(self, text):
        crypto = DES3.new(key=self.key, mode=DES3.MODE_ECB)
        return self.__decrypt(crypto, text)

    def cbc_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_CBC, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cbc_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_CBC, iv=self.iv)
        return self.__decrypt(crypto, text)

    def ofb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_OFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def ofb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_OFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def cfb_encrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_CFB, iv=self.iv)
        return self.__encrypt(crypto, text)

    def cfb_decrypt(self, text):
        if '' == self.iv:
            print('iv is null.')
        crypto = DES3.new(key=self.key, mode=DES3.MODE_CFB, iv=self.iv)
        return self.__decrypt(crypto, text)

    def __encrypt(self, crypto, text):
        data = crypto.encrypt(self.padding(text))
        return base64.b64encode(data).decode('utf-8')

    def __decrypt(self, crypto, data):
        text = base64.b64decode(data)
        return self.unpadding(crypto.decrypt(text).decode('utf-8'))


class SMCrypto:

    def ecb_encrypt(self, text):
        crypt_sm4 = CryptSM4()
        crypt_sm4.set_key(key, SM4_ENCRYPT)
        encrypt_value = crypt_sm4.crypt_ecb(text.encode())  # bytes类型
        crypt_sm4.set_key(key, SM4_DECRYPT)
        value = crypt_sm4.crypt_ecb(encrypt_value)  # bytes类型
        return value

    def sm4_ecb_decrypt(self):
        pass

    def sm4_cbc_encrypt(self):
        pass

    def sm4_cbc_decrypt(self):
        pass

    def sm2_encrypt(self):
        pass

    def sm2_decrypt(self):
        pass


crypt = 'aes'
padding = 'pkcs7'
key = 'd8cg8gVakEq9Agup'
key = ''


if __name__ == '__main__':

    try:

        pass
    except Exception as e:
        print(e)
    #
    # encode = 'lkOn4OukfN7mlcF0WZuoVle+O7PKUFERxYVMulVpb4zlWHhXfFYhQc5cjLubG4GhDVYhOqJD0nf00NxW5wY0QOLYoFrXVak8VwcYEKn6ZjqqwgE00utyCwlGidq6b+ETF/6TzRkHsPMx0x3PtMymDM2HSFwNT6Ol8XHp8URc/fHw49F2QzU4D82Xf1u1ckVS54+6RSkm7YJW4MpL+JpgKVs/RZdFxiABWuaj/HWv1d4='
    # decode = base64.b64decode(encode)
    #
    # key = '3B28A629AA2A6DA74CE5075517EA8CE9'
    # crypt_sm4 = CryptSM4()
    # crypt_sm4.set_key(key.encode(), SM4_DECRYPT)
    # decrypt_value = crypt_sm4.crypt_ecb(decode)  # bytes类型
    # print(decrypt_value)

    # aes = AESCrypto('d8cg8gVakEq9Agup', 'pkcs5')
    # sss = aes.aes_ecb_decrypt('+5bhWR0KmqqeNQmthVS7i/uW4VkdCpqqnjUJrYVUu4v7luFZHQqaqp41Ca2FVLuLNi0j76OB5HE+gXdR9qCG6A==')

    # md = hashlib.md5('libo'.encode(encoding='UTF-8')).hexdigest()
    # print(md)
    # aes = AESCrypto(md, 'pkcs5')
    # data = aes.aes_ecb_encrypt('ZmxhZ3tDVEZfRGVtb30=')
    # print(data)
    # print(aes.aes_ecb_decrypt(data))
    # aes = AESCrypto('123456789012345678901234', 'pkcs7', '123456789012345678901234')
    # data = aes.aes_cbc_encrypt('123456789')
    # print(data)
    # print(aes.aes_cbc_decrypt(data))

    # key_encoded = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCh5Nk2GLiyQFMIU+h3OEA4UeFbu3dCH5sjd/sLTxxvwjXq7JLqJbt2rCIdzpAXOi4jL+FRGQnHaxUlHUBZsojnCcHvhrz2knV6rXNogt0emL7f7ZMRo8IsQGV8mlKIC9xLnlOQQdRNUssmrROrCG99wpTRRNZjOmLvkcoXdeuaCQIDAQAB'
    # rsax = RSACrypto(key_encoded)
    # print(rsax.public_key_encrypt("fdsfsd"))

    # rsax = RSACrypto('', '010001', 'a1e4d93618b8b240530853e87738403851e15bbb77421f9b2377fb0b4f1c6fc235eaec92ea25bb76ac221dce90173a2e232fe1511909c76b15251d4059b288e709c1ef86bcf692757aad736882dd1e98bedfed9311a3c22c40657c9a52880bdc4b9e539041d44d52cb26ad13ab086f7dc294d144d6633a62ef91ca1775eb9a09')
    # print(rsax.modulus_encrypt('fdsfsd'))
